﻿using log4net;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace NVRCsharpDemo
{
    public class Device
    {
        public Int32 uid { get; set; }
        public CHCNetSDK.NET_DVR_DEVICEINFO_V30 deviceInfo { get; set; }
        public String serial { get; set; }
        public Int32 realHandle { get; set; }
        public uint channelNum { get; set; }
        public int[] channels { get; set; }
        public int alarmHandle { get; set; }
        public string ipAddress { get; set; }
        public string username { get; set; }
        public string password { get; set; }

        public Device()
        {
            this.uid = -1;
            this.realHandle = -1;
            this.alarmHandle = -1;
            this.serial = "";
        }

        public static Device GetEnterDevice()
        {
            return new Device
            {
                ipAddress = ConfigurationManager.AppSettings["enterDevice.ipAddress"],
                username = ConfigurationManager.AppSettings["enterDevice.username"],
                password = ConfigurationManager.AppSettings["enterDevice.password"]
            };
        }

        public static Device GetExitDevice()
        {
            return new Device
            {
                ipAddress = ConfigurationManager.AppSettings["exitDevice.ipAddress"],
                username = ConfigurationManager.AppSettings["exitDevice.username"],
                password = ConfigurationManager.AppSettings["exitDevice.password"]
            };
        }
    }

    public class CarInfo
    {
        public int id { get; set; }
        public String plateNumber { get; set; }
        public String vehicleType { get; set; }
        public String enterTime { get; set; }
        public String exitTime { get; set; }

        public CarInfo()
        {

        }
        public CarInfo(String plateNumber, String vehicleType, String enterTime)
        {
            this.plateNumber = plateNumber;
            this.vehicleType = vehicleType;
            this.enterTime = enterTime;
            this.exitTime = null;
        }

        public CarInfo(String plateNumber, String vehicleType, String enterTime, String exitTime)
        {
            this.plateNumber = plateNumber;
            this.vehicleType = vehicleType;
            this.enterTime = enterTime;
            this.exitTime = exitTime;
        }
    }
    public interface AlarmInfo
    {
        int AlarmType { get; }
        int AlarmInputNumber { get; }
        int[] AlarmRelateChannel { get; }
        int[] DiskNumber { get; }
        int[] Channel { get;}
    }

    public class AlarmInfoCommon : AlarmInfo
    {
        private CHCNetSDK.NET_DVR_ALARMINFO info;

        public AlarmInfoCommon(CHCNetSDK.NET_DVR_ALARMINFO info)
        {
            this.info = info;
        }

        public int AlarmType => info.dwAlarmType;


        public int AlarmInputNumber => info.dwAlarmInputNumber;

        public int[] AlarmRelateChannel => info.dwAlarmRelateChannel;

        public int[] DiskNumber => info.dwDiskNumber;

        public int[] Channel => info.dwChannel;
    }

    public class AlarmInfoV30 : AlarmInfo
    {
        private CHCNetSDK.NET_DVR_ALARMINFO_V30 info;

        public AlarmInfoV30(CHCNetSDK.NET_DVR_ALARMINFO_V30 info)
        {
            this.info = info;
        }

        public int AlarmType => (int) info.dwAlarmType;


        public int AlarmInputNumber => (int) info.dwAlarmInputNumber;

        public int[] AlarmRelateChannel => ByteToInt(info.byAlarmRelateChannel);

        public int[] DiskNumber => ByteToInt(info.byDiskNumber);

        public int[] Channel => ByteToInt(info.byChannel);

        private int[] ByteToInt(byte[] bytes)
        {
            int[] rs = new int[bytes.Length];
            for (int i = 0; i < bytes.Length; i++)
            {
                rs[i] = bytes[i];
            }
            return rs;
        }
    }
    public class MessageHandler
    {
        private DbWorker dbWorker = new DbWorker();
        private ILog logger = LogManager.GetLogger("MessageHandler");
        public Device EnterDevice { get; internal set; }
        public Device ExitDevice { get; internal set; }

        public void MessageCallBack(int lCommand, ref CHCNetSDK.NET_DVR_ALARMER pAlarmer, IntPtr pAlarmInfo, uint dwBufLen, IntPtr pUser)
        {
            try
            {
                //通过lCommand来判断接收到的报警信息类型，不同的lCommand对应不同的pAlarmInfo内容
                switch (lCommand)
                {
                    case CHCNetSDK.COMM_ALARM: //(DS-8000老设备)移动侦测、视频丢失、遮挡、IO信号量等报警信息
                        ProcessCommAlarm(ref pAlarmer, pAlarmInfo, dwBufLen, pUser);
                        break;
                    case CHCNetSDK.COMM_ALARM_V30://移动侦测、视频丢失、遮挡、IO信号量等报警信息
                        ProcessCommAlarm_V30(ref pAlarmer, pAlarmInfo, dwBufLen, pUser);
                        break;
                    case CHCNetSDK.COMM_UPLOAD_PLATE_RESULT://交通抓拍结果上传(老报警信息类型)
                        ProcessCommAlarm_Plate(ref pAlarmer, pAlarmInfo, dwBufLen, pUser);
                        break;
                    case CHCNetSDK.COMM_ITS_PLATE_RESULT://交通抓拍结果上传(新报警信息类型)
                        ProcessCommAlarm_ITSPlate(ref pAlarmer, pAlarmInfo, dwBufLen, pUser);
                        break;
                    default:
                        break;
                }
            } catch (Exception e)
            {
                logger.Info("message call back.", e);
            }
        }

        public void ProcessCommAlarm(ref CHCNetSDK.NET_DVR_ALARMER pAlarmer, IntPtr pAlarmInfo, uint dwBufLen, IntPtr pUser)
        {
            CHCNetSDK.NET_DVR_ALARMINFO strutAlarmInfo = new CHCNetSDK.NET_DVR_ALARMINFO();

            strutAlarmInfo = (CHCNetSDK.NET_DVR_ALARMINFO)Marshal.PtrToStructure(pAlarmInfo, typeof(CHCNetSDK.NET_DVR_ALARMINFO));
            AlarmInfo alarmInfo = new AlarmInfoCommon(strutAlarmInfo);
            InteralProcessAlarm(pAlarmer.sDeviceIP, alarmInfo, dwBufLen, pUser);

        }

        public void ProcessCommAlarm_V30(ref CHCNetSDK.NET_DVR_ALARMER pAlarmer, IntPtr pAlarmInfo, uint dwBufLen, IntPtr pUser)
        {
            CHCNetSDK.NET_DVR_ALARMINFO_V30 strutAlarmInfoV30 = new CHCNetSDK.NET_DVR_ALARMINFO_V30();
            strutAlarmInfoV30 = (CHCNetSDK.NET_DVR_ALARMINFO_V30)Marshal.PtrToStructure(pAlarmInfo, typeof(CHCNetSDK.NET_DVR_ALARMINFO_V30));
            AlarmInfo alarmInfo = new AlarmInfoV30(strutAlarmInfoV30);
            InteralProcessAlarm(pAlarmer.sDeviceIP, alarmInfo, dwBufLen, pUser);
        }

        private void InteralProcessAlarm(String strIP, AlarmInfo alarmInfo, uint dwBufLen, IntPtr pUser)
        {
            string stringAlarm = "";
            int i = 0;

            switch (alarmInfo.AlarmType)
            {
                case 0:
                    stringAlarm = "信号量报警，报警报警输入口：" + alarmInfo.AlarmInputNumber + "，触发录像通道：";
                    for (i = 0; i < CHCNetSDK.MAX_CHANNUM; i++)
                    {
                        if (alarmInfo.AlarmRelateChannel[i] == 1)
                        {
                            stringAlarm += (i + 1) + " \\ ";
                        }
                    }
                    break;
                case 1:
                    stringAlarm = "硬盘满，报警硬盘号：";
                    for (i = 0; i < CHCNetSDK.MAX_DISKNUM; i++)
                    {
                        if (alarmInfo.DiskNumber[i] == 1)
                        {
                            stringAlarm += (i + 1) + " \\ ";
                        }
                    }
                    break;
                case 2:
                    stringAlarm = "信号丢失，报警通道：";
                    for (i = 0; i < CHCNetSDK.MAX_CHANNUM; i++)
                    {
                        if (alarmInfo.Channel[i] == 1)
                        {
                            stringAlarm += (i + 1) + " \\ ";
                        }
                    }
                    break;
                case 3:
                    stringAlarm = "移动侦测，报警通道：";
                    for (i = 0; i < CHCNetSDK.MAX_CHANNUM; i++)
                    {
                        if (alarmInfo.Channel[i] == 1)
                        {
                            stringAlarm += (i + 1) + " \\ ";
                        }
                    }
                    break;
                case 4:
                    stringAlarm = "硬盘未格式化，报警硬盘号：";
                    for (i = 0; i < CHCNetSDK.MAX_DISKNUM; i++)
                    {
                        if (alarmInfo.DiskNumber[i] == 1)
                        {
                            stringAlarm += (i + 1) + " \\ ";
                        }
                    }
                    break;
                case 5:
                    stringAlarm = "读写硬盘出错，报警硬盘号：";
                    for (i = 0; i < CHCNetSDK.MAX_DISKNUM; i++)
                    {
                        if (alarmInfo.DiskNumber[i] == 1)
                        {
                            stringAlarm += (i + 1) + " \\ ";
                        }
                    }
                    break;
                case 6:
                    stringAlarm = "遮挡报警，报警通道：";
                    for (i = 0; i < CHCNetSDK.MAX_CHANNUM; i++)
                    {
                        if (alarmInfo.Channel[i] == 1)
                        {
                            stringAlarm += (i + 1) + " \\ ";
                        }
                    }
                    break;
                case 7:
                    stringAlarm = "制式不匹配，报警通道";
                    for (i = 0; i < CHCNetSDK.MAX_CHANNUM; i++)
                    {
                        if (alarmInfo.Channel[i] == 1)
                        {
                            stringAlarm += (i + 1) + " \\ ";
                        }
                    }
                    break;
                case 8:
                    stringAlarm = "非法访问";
                    break;
                default:
                    stringAlarm = "其他未知报警信息";
                    break;
            }
            //创建该控件的主线程直接更新信息列表 
            Console.WriteLine("{0} - {1}: {2}", DateTime.Now.ToString(), strIP, stringAlarm);
        }

        private void ProcessCommAlarm_Plate(ref CHCNetSDK.NET_DVR_ALARMER pAlarmer, IntPtr pAlarmInfo, uint dwBufLen, IntPtr pUser)
        {
            CHCNetSDK.NET_DVR_PLATE_RESULT struPlateResultInfo = new CHCNetSDK.NET_DVR_PLATE_RESULT();
            uint dwSize = (uint)Marshal.SizeOf(struPlateResultInfo);

            struPlateResultInfo = (CHCNetSDK.NET_DVR_PLATE_RESULT)Marshal.PtrToStructure(pAlarmInfo, typeof(CHCNetSDK.NET_DVR_PLATE_RESULT));
            //报警设备IP地址
            string strIP = pAlarmer.sDeviceIP;
            String serial = Encoding.UTF8.GetString(pAlarmer.sSerialNumber);

            //抓拍时间：年月日时分秒
            string strTimeYear = System.Text.Encoding.UTF8.GetString(struPlateResultInfo.byAbsTime);
            string plateNumber = struPlateResultInfo.struPlateInfo.sLicense;
            byte byVehicleType = struPlateResultInfo.struVehicleInfo.byVehicleType;

            //上传结果
            string stringAlarm = "抓拍上传，"
                + "车牌：" + plateNumber
                + "，车辆序号：" + struPlateResultInfo.struVehicleInfo.dwIndex
                + "，车辆类型：" + (byVehicleType == 1 ? "小型车" : "大型车");
            String vehicleType = "小型车";
            switch (byVehicleType)
            {
                case 0:
                case 1:
                    vehicleType = "小型车";
                    break;
                case 2:
                    vehicleType = "大型车";
                    break;
                case 3:
                    vehicleType = "行人触发";
                    break;
                case 4:
                    vehicleType = "二轮车触发";
                    break;
                case 5:
                    vehicleType = "三轮车触发";
                    break;
                case 6:
                    vehicleType = "机动车触发";
                    break;
                default:
                    vehicleType = "未知";
                    break;

            }
            if (byVehicleType >= 3)
            {
                logger.InfoFormat("非机动车辆触发，不记录到数据库中。{0}", vehicleType);
                return;
            }
            if (EnterDevice.serial.Equals(serial))
            {
                dbWorker.Insert(plateNumber, vehicleType, strTimeYear);
            }
            else if (ExitDevice.serial.Equals(serial))
            {
                dbWorker.Update(plateNumber, strTimeYear);
            }
            SaveDisplayFile();
            logger.InfoFormat("{0} - {1}: {2}", DateTime.Now.ToString(), strIP, stringAlarm);
        }

        private void ProcessCommAlarm_ITSPlate(ref CHCNetSDK.NET_DVR_ALARMER pAlarmer, IntPtr pAlarmInfo, uint dwBufLen, IntPtr pUser)
        {
            CHCNetSDK.NET_ITS_PLATE_RESULT struITSPlateResult = new CHCNetSDK.NET_ITS_PLATE_RESULT();
            uint dwSize = (uint)Marshal.SizeOf(struITSPlateResult);

            struITSPlateResult = (CHCNetSDK.NET_ITS_PLATE_RESULT)Marshal.PtrToStructure(pAlarmInfo, typeof(CHCNetSDK.NET_ITS_PLATE_RESULT));
            logger.Info("捕获一个车牌");
            //报警设备IP地址
            string strIP = pAlarmer.sDeviceIP;
            String serial = Encoding.UTF8.GetString(pAlarmer.sSerialNumber);
            logger.InfoFormat("捕获车牌的IP{0}和序列号{1}", strIP, serial);
            //抓拍时间：年月日时分秒
            string strTimeYear = string.Format("{0:D4}", struITSPlateResult.struSnapFirstPicTime.wYear) +
                string.Format("{0:D2}", struITSPlateResult.struSnapFirstPicTime.byMonth) +
                string.Format("{0:D2}", struITSPlateResult.struSnapFirstPicTime.byDay) + " "
                + string.Format("{0:D2}", struITSPlateResult.struSnapFirstPicTime.byHour) + ":"
                + string.Format("{0:D2}", struITSPlateResult.struSnapFirstPicTime.byMinute) + ":"
                + string.Format("{0:D2}", struITSPlateResult.struSnapFirstPicTime.bySecond) + ":"
                + string.Format("{0:D3}", struITSPlateResult.struSnapFirstPicTime.wMilliSec);
            string plateNumber = struITSPlateResult.struPlateInfo.sLicense;
            byte byVehicleType = struITSPlateResult.struVehicleInfo.byVehicleType;

            //上传结果
            string stringAlarm = "抓拍上传，"
                + "车牌：" + plateNumber
                + "，车辆序号：" + struITSPlateResult.struVehicleInfo.dwIndex
                + "，车辆类型：" + (byVehicleType == 1 ? "小型车" : "大型车");
            logger.InfoFormat("捕获车牌: {0} - {1}: {2}", DateTime.Now.ToString(), strIP, stringAlarm);
            String vehicleType = "小型车";
            switch (byVehicleType)
            {
                case 0:
                case 1:
                    vehicleType = "小型车";
                    break;
                case 2:
                    vehicleType = "大型车";
                    break;
                case 3:
                    vehicleType = "行人触发";
                    break;
                case 4:
                    vehicleType = "二轮车触发";
                    break;
                case 5:
                    vehicleType = "三轮车触发";
                    break;
                case 6:
                    vehicleType = "机动车触发";
                    break;
                default:
                    vehicleType = "未知";
                    break;

            }
            if (byVehicleType >= 3)
            {
                logger.InfoFormat("非机动车辆触发，不记录到数据库中。{0}", vehicleType);
                return;
            }
            if (EnterDevice.serial.Equals(serial))
            {
                dbWorker.Insert(plateNumber, vehicleType, strTimeYear);
            }
            else if (ExitDevice.serial.Equals(serial))
            {
                dbWorker.Update(plateNumber, strTimeYear);
            }
            SaveDisplayFile();
        }

        internal void Reset()
        {
            dbWorker.Reset();
            SaveDisplayFile();
        }

        public System.Action NotifyUpdateCarInfo;

        private void SaveDisplayFile()
        {
            int totalCarport = Int32.Parse(ConfigurationManager.AppSettings["totalCarport"]);
            int bigCarport = Int32.Parse(ConfigurationManager.AppSettings["bigCarport"]);
            int smallCarport = Int32.Parse(ConfigurationManager.AppSettings["smallCarport"]);
            int bigCarNum = dbWorker.QueryBigCarCount();
            int smallCarNum = dbWorker.QuerySmallCarCount();
            RemainCarport remainCarport = new RemainCarport()
            {
                total = totalCarport,
                remain = totalCarport - bigCarNum - smallCarNum,
                remainBig = bigCarport - bigCarNum,
                remainSmall = smallCarport - smallCarNum
            };
            remainCarport.Save();
            NotifyUpdateCarInfo();
        }
        Random random = new Random();
        public void run()
        {
            for (int i = 0; i < 100; i++)
            {
                if (random.Next(2) == 0)
                    addCar();
                else
                    removeCar();
                Thread.Sleep(1000);
                SaveDisplayFile();
            }
        }

        private void removeCar()
        {
            int number = dbWorker.QueryExistCarCount(null);
            number = random.Next(number);
            try
            {
                CarInfo carInfo = dbWorker.QueryExistedCar(number, 1)[0];
                dbWorker.Update(carInfo.plateNumber, DateTime.Now.ToString("s"));
            }
            catch (Exception e)
            {

            }
        }

        private void addCar()
        {
            String[] types = { "小型车", "大型车" };
            dbWorker.Insert("川A" + RandomString(5), types[random.Next(2)], DateTime.Now.ToString("s"));
        }

        private string RandomString(int size)
        {
            StringBuilder builder = new StringBuilder();
            char ch;
            for (int i = 0; i < size; i++)
            {
                ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
                builder.Append(ch);
            }

            return builder.ToString();
        }
    }

    public class RemainCarport
    {
        private string totalPath;
        private string remainTotalPath;
        private string remainBigPath;
        private string remainSmallPath;

        public int total { get; set; }
        public int remain { get; set; }
        public int remainBig { get; set; }
        public int remainSmall { get; set; }

        public RemainCarport()
        {
            totalPath = ConfigurationManager.AppSettings["totalPath"];
            remainTotalPath = ConfigurationManager.AppSettings["remainTotalPath"];
            remainBigPath = ConfigurationManager.AppSettings["remainBigPath"];
            remainSmallPath = ConfigurationManager.AppSettings["remainSmallPath"];
        }

        public void Save()
        {
            saveFile(totalPath, total);
            saveFile(remainTotalPath, remain);
            saveFile(remainBigPath, remainBig);
            saveFile(remainSmallPath, remainSmall);
        }

        private void saveFile(String path, int number)
        {
            using (StreamWriter file = File.CreateText(path))
            {
                file.WriteLine(number);
            }
        }
    }
    
}
